<!DOCTYPE html>
<html lang="zh-cn">
<he
    <link type="text/css" rel="stylesheet" href="/bundles/blog-common.css?v=KOZafwuaDasEedEenI5aTy8aXH0epbm6VUJ0v3vsT_Q1"/>
<link id="MainCss" type="text/css" rel="stylesheet" href="/skins/ThinkInside/bundle-ThinkInside.css?v=RRjf6pEarGnbXZ86qxNycPfQivwSKWRa4heYLB15rVE1"/>
<link type="text/css" rel="stylesheet" href="/blog/customcss/428549.css?v=%2fam3bBTkW5NBWhBE%2fD0lcyJv5UM%3d"/>

</head>
<body>
<a name="top"></a>

<div id="page_begin_html"></div><script>load_page_begin_html();</script>

<div id="topics">
	<div class = "post">
		<h1 class = "postTitle">
			<a id="cb_post_title_url" class="postTitle2" href="https://www.cnblogs.com/frankdeng/p/9301760.html">Spark（十六）DataSet</a>
		</h1>
		<div class="clear"></div>
		<div class="postBody">
			<div id="cnblogs_post_body" class="blogpost-body"><h2>&nbsp;</h2>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Spark最吸引开发者的就是简单易用、跨语言(Scala, Java, Python, and R)的API。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">本文主要讲解Apache Spark 2.0中RDD，DataFrame和Dataset三种API；它们各自适合的使用场景；它们的性能和优化；列举使用DataFrame和DataSet代替RDD的场景。本文聚焦DataFrame和Dataset，因为这是Apache Spark 2.0的API统一的重点。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Apache Spark 2.0统一API的主要动机是：简化Spark。通过减少用户学习的概念和提供结构化的数据进行处理。除了结构化，Spark也提供higher-level抽象和API作为特定领域语言(DSL)。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">弹性数据集(RDD)</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">RDD是Spark建立之初的核心API。RDD是不可变分布式弹性数据集，在Spark集群中可跨节点分区，并提供分布式low-level API来操作RDD，包括transformation和action。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">何时使用RDD？</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">使用RDD的一般场景：</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你需要使用low-level的transformation和action来控制你的数据集；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你的数据集非结构化，比如：流媒体或者文本流；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你想使用函数式编程来操作你的数据，而不是用特定领域语言(DSL)表达；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你不想加入schema，比如，当通过名字或者列处理(或访问)数据属性不在意列式存储格式；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">当你可以放弃使用DataFrame和Dataset来优化结构化和半结构化数据集的时候。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">在Spark2.0中RDD发生了什么</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你可能会问：RDD是不是成为&ldquo;二等公民&rdquo;了？或者是不是干脆以后不用了？</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">答案当然是NO！</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">通过后面的描述你会得知：Spark用户可以在RDD，DataFrame和Dataset三种数据集之间无缝转换，而且只需要使用超级简单的API方法。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">DataFrames</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">DataFrame与RDD相同之处，都是不可变分布式弹性数据集。不同之处在于，DataFrame的数据集都是按指定列存储，即结构化数据。类似于传统数据库中的表。DataFrame的设计是为了让大数据处理起来更容易。DataFrame允许开发者把结构化数据集导入DataFrame，并做了higher-level的抽象；DataFrame提供特定领域的语言(DSL)API来操作你的数据集。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">在Spark2.0中，DataFrame API将会和Dataset API合并，统一数据处理API。由于这个统一&ldquo;有点急&rdquo;，导致大部分Spark开发者对Dataset的high-level和type-safe API并没有什么概念。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085248001-2145624378.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f12.baidu.com/it/u=2302911671,884460051&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=358&amp;s=0196E4320C30CA885DD16B570200D0E6&amp;access=215967316" alt="" data-index="1" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">DataSets</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">从Spark2.0开始，DataSets扮演了两种不同的角色：强类型API和弱类型API，见下表。从概念上来讲，可以把DataFrame 当作一个泛型对象的集合DataSet[Row], Row是一个弱类型JVM 对象。相对应地，如果JVM对象是通过Scala的case class或者Java class来表示的，Dataset是强类型的。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085312922-924476146.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f10.baidu.com/it/u=427266621,2456188776&amp;fm=173&amp;app=25&amp;f=JPEG?w=639&amp;h=173&amp;s=C992E71245F8FF24527CA5D90200D0FB&amp;access=215967316" alt="" data-index="2" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Dataset API的优势</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">对于Spark开发者而言，你将从Spark 2.0的DataFrame和Dataset统一的API获得以下好处：</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">1. 静态类型和运行时类型安全</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">考虑静态类型和运行时类型安全，SQL有很少的限制而Dataset限制很多。例如，Spark SQL查询语句，你直到运行时才能发现语法错误(syntax error)，代价较大。然后DataFrame和Dataset在编译时就可捕捉到错误，节约开发时间和成本。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085331924-771834854.png" alt="" /></span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Dataset API都是lambda函数和JVM typed object，任何typed-parameters不匹配即会在编译阶段报错。因此使用Dataset节约开发时间。</span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f10.baidu.com/it/u=2138925894,3551455902&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=268&amp;s=8CAA7C329BE84D091E5420CF0000E0B2&amp;access=215967316" alt="" data-index="3" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">2. High-level抽象以及结构化和半结构化数据集的自定义视图</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">DataFrame是Dataset[Row]的集合，把结构化数据集视图转换成半结构化数据集。例如，有个海量IoT设备事件数据集，用JSON格式表示。JSON是一个半结构化数据格式，很适合使用Dataset, 转成强类型的Dataset[DeviceIoTData]。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">使用Scala为JSON数据DeviceIoTData定义case class。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085353927-832334538.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f10.baidu.com/it/u=2138571927,3069036914&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=203&amp;s=8850C5129FF44C0342F86DD90200C0B1&amp;access=215967316" alt="" data-index="4" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">紧接着，从JSON文件读取数据</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085408925-1130338287.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f12.baidu.com/it/u=4042382231,3763326363&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=118&amp;s=4010CD324D266F205A4520D6030080B3&amp;access=215967316" alt="" data-index="5" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">上面代码运行时底层会发生下面3件事。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Spark读取JSON文件，推断出其schema，创建一个DataFrame；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Spark把数据集转换DataFrame -&gt; Dataset[Row]，泛型Row object，因为这时还不知道其确切类型；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">Spark进行转换：Dataset[Row] -&gt; Dataset[DeviceIoTData]，DeviceIoTData类的Scala JVM object</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">我们的大多数人，在操作结构化数据时，都习惯于以列的方式查看和处理数据列，或者访问对象的指定列。Dataset 是Dataset[ElementType]类型对象的集合，既可以编译时类型安全，也可以为强类型的JVM对象定义视图。从上面代码获取到的数据可以很简单的展示出来，或者用高层方法处理。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085428923-18939443.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f12.baidu.com/it/u=1220126225,962896050&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=238&amp;s=3C28743213B140224CF5F4C60100E0B2&amp;access=215967316" alt="" data-index="6" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">3. 简单易用的API</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">虽然结构化数据会给Spark程序操作数据集带来挺多限制，但它却引进了丰富的语义和易用的特定领域语言。大部分计算可以被Dataset的high-level API所支持。例如，简单的操作agg，select，avg，map，filter或者groupBy即可访问DeviceIoTData类型的Dataset。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">使用特定领域语言API进行计算是非常简单的。例如，使用filter()和map()创建另一个Dataset。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">把计算过程翻译成领域API比RDD的关系代数式表达式要容易的多。例如：</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085445927-1595204981.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f11.baidu.com/it/u=3591501211,3909609158&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=309&amp;s=8450ED320BC84C415A45A4DA0000C0B3&amp;access=215967316" alt="" data-index="7" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">4. 性能和优化</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">使用DataFrame和Dataset API获得空间效率和性能优化的两个原因：</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">首先：因为DataFrame和Dataset是在Spark SQL 引擎上构建的，它会使用Catalyst优化器来生成优化过的逻辑计划和物理查询计划。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">R，Java，Scala或者Python的DataFrame/Dataset API，所有的关系型的查询都运行在相同的代码优化器下，代码优化器带来的的是空间和速度的提升。不同的是Dataset[T]强类型API优化数据引擎任务，而弱类型API DataFrame在交互式分析场景上更快，更合适。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p"><img src="https://img2018.cnblogs.com/blog/1385722/201809/1385722-20180905085503922-1231333747.png" alt="" /></span></p>
<div class="contentMedia contentPadding">
<div class="contentImg linexRBS">
<div><img src="https://f11.baidu.com/it/u=3497794975,3480846326&amp;fm=173&amp;app=25&amp;f=JPEG?w=640&amp;h=368&amp;s=E1BA2C73012A57096FDC21D30000C0B3&amp;access=215967316" alt="" data-index="8" /></div>
</div>
</div>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">其次，通过博客https://databricks.com/blog/2016/05/23/apache-spark-as-a-compiler-joining-a-billion-rows-per-second-on-a-laptop.html 可以知道:Dataset能使用Encoder映射特定类型的JVM 对象到Tungsten内部内存表示。Tungsten的Encoder可以有效的序列化/反序列化JVM object，生成字节码来提高执行速度。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">什么时候使用DataFrame或者Dataset？</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你想使用丰富的语义，high-level抽象，和特定领域语言API，那你可以使用DataFrame或者Dataset；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你处理的半结构化数据集需要high-level表达，filter，map，aggregation，average，sum，SQL查询，列式访问和使用lambda函数，那你可以使用DataFrame或者Dataset；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">想利用编译时高度的type-safety，Catalyst优化和Tungsten的code生成，那你可以使用DataFrame或者Dataset；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你想统一和简化API使用跨Spark的Library，那你可以使用DataFrame或者Dataset；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">如果你是一个R使用者，那你可以使用DataFrame或者Dataset；</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">如果你是一个Python使用者，那你可以使用DataFrame或者Dataset。</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">你可以无缝地把DataFrame或者Dataset转化成一个RDD，只需简单的调用.rdd：</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">总结</span></p>
<p class="contentText contentSize  contentPadding"><span class="bjh-p">通过上面的分析，什么情况选择RDD，DataFrame还是Dataset已经很明显了。RDD适合需要low-level函数式编程和操作数据集的情况；DataFrame和Dataset适合结构化数据集，使用high-level和特定领域语言(DSL)编程，空间效率高和速度快。</span></p></div>

</body>
</html>
